package com.itjeffrey.autocode.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.itjeffrey.autocode.bo.ProcessDefied;
import com.itjeffrey.autocode.common.PageDomain;
import com.itjeffrey.autocode.common.PageResult;
import com.itjeffrey.autocode.common.Result;
import com.itjeffrey.autocode.constant.SysConstant;
import com.itjeffrey.autocode.util.BeanOprUtils;
import com.itjeffrey.autocode.util.DateUtil;
import com.itjeffrey.autocode.bo.req.ProcessDeploymentBO;
import com.itjeffrey.autocode.util.ImageUtil;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Describe: 流程定义控制器
 */
@RestController
@RequestMapping(ControllerConstants.PROCESS_URL_PREFIX + "defined")
public class ProcessDefinedController extends ControllerConstants {

    private static Logger log = LoggerFactory.getLogger(ProcessDefinedController.class);

    /**
     * 用户目录
     */
    private String resourcePath = System.getProperty(SysConstant.USER_DIR);

    @Resource
    private ObjectMapper objectMapper;

    /**
     * 流程部署服务
     */
    @Resource
    private RepositoryService repositoryService;

    /**
     * 运行时服务
     */
    @Resource
    private RuntimeService runtimeService;

    /**
     * 历史信息服务
     */
    @Resource
    private HistoryService historyService;

    /**
     * 流程部署（发布）
     * @param deployment
     * @return
     */
    @PostMapping("/deploy")
    public ResponseEntity<Result> deploy(@RequestBody ProcessDeploymentBO deployment) {
        log.info("ProcessDefinedController deploy===process deploy start===deployKey:{}===datetime:{}",
                deployment.getDeployKey(), DateUtil.getCurTime());
        String xmlFileName = deployment.getResourceName() + ".bpmn20.xml";
        String pngFileName = deployment.getResourceName() + ".png";
        File xmlFile = new File(resourcePath + xmlFileName);
        File pngFile = new File(resourcePath + pngFileName);
        ImageUtil.convertToPng(deployment.getSvg(), pngFile);

        InputStream xmlInputStream = null;
        InputStream pngInputStream = null;
        try {
            FileCopyUtils.copy(deployment.getXml().getBytes(), xmlFile);
            xmlInputStream = new FileInputStream(xmlFile);
            pngInputStream = new FileInputStream(pngFile);
        } catch (IOException e) {
        }
        repositoryService.createDeployment()
                .addInputStream(xmlFileName, xmlInputStream)
                .addInputStream(pngFileName, pngInputStream)
                .key(deployment.getDeployKey())
                .name(deployment.getDeployName())
                .deploy();
        log.info("ProcessDefinedController deploy===process deploy end===deployKey:{}===datetime:{}",
                deployment.getDeployKey(), DateUtil.getCurTime());
        return ResponseEntity.ok(Result.ok());
    }

    /**
     * 草稿流程部署（发布）
     * @param deployment
     * @return
     */
    @PostMapping("/cgdeploy")
    public ResponseEntity<Result> cgdeploy(@RequestBody ProcessDeploymentBO deployment) {
        log.info("ProcessDefinedController cgdeploy===process cgdeploy start===modelId:{}===datetime:{}",
                deployment.getModelId(), DateUtil.getCurTime());
        //TODO 后期优化
        log.info("ProcessDefinedController cgdeploy===process cgdeploy end===modelId:{}===datetime:{}",
                deployment.getModelId(), DateUtil.getCurTime());
        return ResponseEntity.ok(Result.ok());
    }

    /**
     * 模型保存
     * @param deployment
     * @return
     */
    @PostMapping("/save")
    public ResponseEntity<Result> save(@RequestBody ProcessDeploymentBO deployment) {
        log.info("ProcessDefinedController save===process save start===processKey:{}===datetime:{}",
                deployment.getProcessKey(), DateUtil.getCurTime());
        try {
            //1.保存Model
            Model modelData = repositoryService.newModel();

            // 创建model需要的MetaInfo属性数据
            ObjectNode modelObjectNode = objectMapper.createObjectNode();
            modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, deployment.getProcessName());
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);// 版本
            modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION,deployment.getDescription());
            modelData.setMetaInfo(modelObjectNode.toString());

            modelData.setName(deployment.getProcessName());
            modelData.setKey(deployment.getProcessKey());
            modelData.setCategory(""); //分类
            // 保存模型
            repositoryService.saveModel(modelData);

            //2.保存流程文件，bpmn、png
            //保存XML
            repositoryService.addModelEditorSource(modelData.getId(),deployment.getXml().getBytes("UTF-8"));
            //保存PNG
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            String pngFileName = deployment.getResourceName() + ".png";
            File pngFile = new File(resourcePath + pngFileName);
            ImageUtil.convertToPng(deployment.getSvg(), pngFile);
            InputStream pngInputStream = new FileInputStream(pngFile);
            byte[] buff = new byte[1024];
            while ((pngInputStream.read(buff)) != -1) {
                bos.write(buff);
            }
            byte[] result = bos.toByteArray();
            // 添加图片源
            repositoryService.addModelEditorSourceExtra(
                    modelData.getId(), result);

        } catch (IOException e){
            e.printStackTrace();
            log.error("流程保存失败！");
        }
        log.info("ProcessDefinedController save===process save end===processKey:{}===datetime:{}",
                deployment.getProcessKey(), DateUtil.getCurTime());
        return ResponseEntity.ok(Result.ok());
    }

    /**
     * 获取流程定义列表数据
     */
    @PostMapping("data")
    public ResponseEntity<Result> data(@RequestBody PageDomain pageDomain) {
        PageResult<ProcessDefied> pageResult = new PageResult<>();
        BeanOprUtils.copyProperties(pageDomain, pageResult);
        ProcessDefinitionQuery processDefinitionQuery = repositoryService
                .createProcessDefinitionQuery();
        if (StringUtils.hasText(pageDomain.getDefinitionName())) {
            processDefinitionQuery.processDefinitionNameLike(pageDomain.getDefinitionName());
        }
        List<ProcessDefinition> processDefinitions = processDefinitionQuery
                .orderByDeploymentId()
                .desc()
                .orderByProcessDefinitionVersion()
                .desc()
                .listPage(pageDomain.start(), pageDomain.end());

        List<ProcessDefied> data = new ArrayList<>();

        processDefinitions.forEach(processDefinition -> {
            ProcessDefied defined = new ProcessDefied();
            defined.setId(processDefinition.getId());
            defined.setName(processDefinition.getName());
            defined.setVersion(processDefinition.getVersion());
            defined.setKey(processDefinition.getKey());
            defined.setBpmn(processDefinition.getResourceName());
            defined.setPng(processDefinition.getDiagramResourceName());
            defined.setDeploymentId(processDefinition.getDeploymentId());
            long instanceCount =
                    historyService.createHistoricActivityInstanceQuery().processDefinitionId(processDefinition.getId()).count();
            if(instanceCount > 0){
                //若此流程定义ID下没有流程实例则流程实例未启动(流程定义未发布)
                defined.setStatus(1);
            }else {
                defined.setStatus(0);
            }
            data.add(defined);
        });

        long count = processDefinitionQuery.orderByProcessDefinitionVersion().asc().count();
        pageResult.setTotals((int) count);
        pageResult.setData(data);
        return ResponseEntity.ok(Result.ok(pageResult));
    }

    /**
     * 根据 Id 删除流程定义
     */
    @PostMapping("remove/{id}")
    public ResponseEntity<Result> remove(@PathVariable("id") String deploymentId) {
        repositoryService.deleteDeployment(deploymentId, true);
        return ResponseEntity.ok(Result.ok());
    }

    /**
     * 获取流程模型列表视图
     */
    @GetMapping("/resource")
    public ResponseEntity<Result> getProcessDefineResource(HttpServletResponse response,
                                                           @RequestParam("deploymentId") String deploymentId,
                                                           @RequestParam("resourceName") String resourceName,
                                                           @RequestParam("type") String type) {
        log.info("ProcessDefinedController getProcessDefineResource " +
                        "Start===deploymentId:{}====resourceName:{}===datetime:{}",
                deploymentId, resourceName, DateUtil.getCurTime());
        InputStream inputStream = getProcessDefineResource(deploymentId, resourceName);
        byte[] bytes = new byte[1024];
        try {
            if("png".equals(type)){
                response.setContentType("image/png");
            }else if("bpmn".equals(type)){
                response.setContentType("text/xml");
            }
            response.setCharacterEncoding("utf-8");
            OutputStream outputStream = response.getOutputStream();
            while (inputStream.read(bytes) != -1) {
                outputStream.write(bytes);
            }
            outputStream.flush();
            inputStream.close();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("ProcessDefinedController getProcessDefineResource " +
                        "End===processDefineId:{}====resourceName:{}===datetime:{}",
                deploymentId, resourceName, DateUtil.getCurTime());
        return ResponseEntity.status(HttpStatus.OK.value()).build();
    }

    /**
     * 获取流程资源文件
     */
    private InputStream getProcessDefineResource(String deploymentId, String resourceName) {
        InputStream resourceAsStream = null;
        try {
            resourceAsStream = repositoryService.getResourceAsStream(deploymentId, resourceName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resourceAsStream;
    }
}
